using System.Collections.Concurrent;
using System.Net.NetworkInformation;
using Flurl.Http;

namespace IpRange;

public class NetworkLatencyTester
{
    private readonly string _inputFile;
    private readonly string _reportFile;
    private const int BatchSize = 500; // 每批处理500个IP
    private const int TimeoutMs = 1000;
    private readonly ConcurrentBag<(string Ip, long Latency)> _results = [];

    public event Action<string, long>? OnProgressed;
    public event Action<ConcurrentBag<(string Ip, long Latency)>>? OnCompleted;

    public NetworkLatencyTester(string inputFile, string reportFile)
    {
        _inputFile = inputFile;
        _reportFile = reportFile;
    }

    public async Task RunTestAsync()
    {
        await using StreamWriter writer = new(_reportFile);
        BlockingCollection<string> ipQueue = new();
        CancellationTokenSource cancellationToken = new();

        Task producer = Task.Run(() =>
        {
            foreach (string ip in File.ReadLines(_inputFile))
            {
                ipQueue.Add(ip, cancellationToken.Token);
                if (ipQueue.Count >= 10_000) Thread.Sleep(50); // 流量控制
            }

            ipQueue.CompleteAdding();
        }, cancellationToken.Token);

        Task processor = Task.Run(() =>
        {
            ParallelOptions options = new()
            {
                MaxDegreeOfParallelism = 500,
                CancellationToken = cancellationToken.Token,
            };

            Parallel.ForEachAsync(GetBatches(ipQueue), options, async (batch, ct) =>
            {
                using Ping ping = new();
                foreach (string ip in batch)
                {
                    try
                    {
                        PingReply reply = await ping.SendPingAsync(ip, TimeoutMs);
                        if (reply.Status != IPStatus.Success) continue;
                        if (!await GetResultAsync(ip)) continue;
                        long latency = reply.RoundtripTime;
                        _results.Add((ip, latency));
                        OnProgressed?.Invoke(ip, latency);
                    }
                    catch
                    {
                        /* 错误处理 */
                    }
                }
            }).ConfigureAwait(false);
        }, cancellationToken.Token);

        await Task.WhenAll(producer, processor);

        // 生成报告
        foreach ((string Ip, long Latency) result in _results)
        {
            await writer.WriteLineAsync($"{result.Ip},{result.Latency}");
        }

        OnCompleted?.Invoke(_results);
    }

    private async Task<bool> GetResultAsync(string ip)
    {
        string url = $"https://{ip}/translate_a/single?client=gtx&sl=zh-CN&tl=en&dt=t&q=你好";
        return (await url
            .WithHeader("host", "translate.googleapis.com")
            .WithTimeout(4)
            .GetStringAsync()).Contains("Hello");
    }

    private IEnumerable<List<string>> GetBatches(BlockingCollection<string> source)
    {
        List<string> batch = new(BatchSize);
        foreach (string ip in source.GetConsumingEnumerable())
        {
            batch.Add(ip);
            if (batch.Count < BatchSize) continue;
            yield return batch;
            batch = new List<string>(BatchSize);
        }

        if (batch.Count > 0) yield return batch;
    }
}